package com.sparrow.ioc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sparrow.constant.CACHE_KEY;
import com.sparrow.constant.SYS_OBJECT_NAME;
import com.sparrow.core.Cache;
import com.sparrow.core.Log;
import com.sparrow.core.asm.Generator4MethodAccessor;
import com.sparrow.core.asm.MethodAccessor;
import com.sparrow.support.Initializer;
import com.sparrow.utils.FileUtil;
import com.sparrow.utils.StringUtil;
import com.sparrow.utils.Xml;

public class BeanFactory {
	/**
	 * beanDefination缓存
	 */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
	/**
	 * 对象缓存
	 */
	private final Map<String, Object> beanFactoryCache = new ConcurrentHashMap<String, Object>();

	/**
	 * asm 生成的代理bean的缓存
	 */
	private final Map<String, MethodAccessor> proxyBeanCache = new ConcurrentHashMap<String, MethodAccessor>();
	/**
	 * bean工场
	 */
	private static BeanFactory beanFactory = new BeanFactory();
	private String xmlName;
	private String systemConfigPath;

	public String getXmlName() {
		return xmlName;
	}

	public void setContextConfig(String xmlName) {
		this.xmlName = xmlName;
	}

	public String getSystemConfigPath() {
		return systemConfigPath;
	}

	public void setSystemConfigPath(String systemConfigPath) {
		this.systemConfigPath = systemConfigPath;
	}

	public static BeanFactory getInstance() {
		return beanFactory;
	}

	private void readProperty(Element el) throws Exception {
		String refBeanName = el.getAttribute("ref").trim();
		Element parentElement = (Element) el.getParentNode();
		String parentBeanName = parentElement.getAttribute("name");
		String parentIsSingleton = parentElement.getAttribute("isSingleton")
				.trim().toLowerCase();
		// 如果当前对象的父对象是单例
		// 则注入该对象
		if (parentIsSingleton.equals("true") || parentIsSingleton.equals("")) {
			// 引用必须在该对象初始化之前先被初始化
			if (this.beanFactoryCache.get(refBeanName) == null) {
				Log.getInstance()
						.warm("error: ref bean "
								+ refBeanName
								+ " must be initialization! because parent Class is Singleton");
			}
			// 注入该对象
			this.setRely(this.getBean(parentBeanName), refBeanName,
					this.getBean(refBeanName));
		} else {
			// 如果非单例对象 则将类定义压入到缓存
			BeanDefinition beanDefinition = this.beanDefinitionMap
					.get(refBeanName);
			if (beanDefinition == null) {
				beanDefinition = new BeanDefinition();
				// 实例化该依赖对象并设置其class
				beanDefinition.setBeanClass(this.getBean(refBeanName)
						.getClass());
			}
			// 获取依赖类并压入缓存
			this.beanDefinitionMap.get(parentBeanName).getRelyOnClass()
					.put(refBeanName, beanDefinition);
		}
	}

	/**
	 * 读xml标签初始化bean
	 * 
	 * 并加入到bean factory 和bean defination facotory
	 * 
	 * @author zhanglizhi
	 * @date 2012-3-25
	 * @version 1.0
	 * @param el
	 */
	private void readBean(Element el) {
		if (el.getTagName().trim().toLowerCase().equals("beanfactory")) {
			return;
		}
		// 是否为单子实例
		String strIsSingleton = el.getAttribute("isSingleton");
		// class名
		String className = el.getAttribute("class");
		// bean名
		String beanName = el.getAttribute("name").trim();
		Class<?> beanClass = null;
		try {
			beanClass = Class.forName(className);
		} catch (Exception e) {
			Log.getInstance().error(e, "bean name error :" + beanName);
			return;
		}
		// 如果是单子对象
		if (strIsSingleton.trim().equals("true")
				|| strIsSingleton.trim().equals("")) {
			try {
				this.beanFactoryCache.put(beanName, beanClass.newInstance());
			} catch (Exception e) {
				Log.getInstance().error(e);
			}
		} else {
			// 如果不是单子则缓存该类的元数据
			BeanDefinition beanDefinition = new BeanDefinition();
			beanDefinition.setBeanClass(beanClass);
			this.beanDefinitionMap.put(beanName, beanDefinition);
			// 如果是非单例对象则生成代理访问对象，提高反射效率
			this.proxyBeanCache.put(
					beanClass.getSimpleName(),
					Generator4MethodAccessor.getInstance().newMothodAccessor(
							beanClass));
		}
	}

	private void initBeanByElement(Element el) throws Exception {
		NodeList nl = el.getChildNodes();
		Node n;
		// 如果子节点>0则先加载子节点对象
		if (nl.getLength() > 0) {
			this.readBean(el);
			for (int i = 0; i < nl.getLength(); i++) {
				n = nl.item(i);
				if (n.getNodeType() == 1) {
					Element e = (Element) n;
					this.initBeanByElement(e);
				}
			}
		} else {
			// 如果没有依赖对象的单独节点
			String strTagName = el.getTagName().trim().toLowerCase();
			if (strTagName.equals("property")) {
				this.readProperty(el);
			} else if (strTagName.equals("bean")) {
				this.readBean(el);
			}
		}
	}

	/**
	 * 注入
	 * 
	 * @author zhanglizhi
	 * @date 2012-3-25
	 * @version 1.0
	 * @param <T>
	 * @param currentObject
	 * @param relyBeanName
	 * @param relyBean
	 * @throws Exception
	 */
	private <T> void setRely(T currentObject, String relyBeanName, T relyBean)
			throws Exception {
		// set方法
		String setBeanMethod;
		// 设置bean类别
		Class<?> setBeanType = relyBean.getClass();
		setBeanMethod = StringUtil.getSetBeanMethodName(relyBeanName);
		Class<?> currentClass = currentObject.getClass();
		if (setBeanType == null) {
			setBeanType = this.beanFactoryCache.get(relyBeanName).getClass();
		}
		Method method = null;
		try {
			method = currentClass.getMethod(setBeanMethod, setBeanType);
		} catch (NoSuchMethodException e) {
			if (method == null) {
				Class<?>[] interfaces = setBeanType.getInterfaces();
				for (int i = 0; i < interfaces.length; i++) {
					try {
						method = currentClass.getMethod(setBeanMethod,
								interfaces[i]);
						break;
					} catch (NoSuchMethodException ex) {

						Class<?>[] superClass = interfaces[i].getInterfaces();
						if (superClass != null && superClass.length > 0) {
							try {
								method = currentClass.getMethod(setBeanMethod,
										superClass[0]);
							} catch (Exception e2) {
								Log.getInstance()
										.error(e2,
												interfaces[i]
														+ " interface not found!");
							}
						}
					}
				}
			}
		}
		if (method != null) {
			method.invoke(currentObject, relyBean);
		}
	}

	public MethodAccessor getProxyBean(String className) {
		return this.proxyBeanCache.get(className);
	}

	public <T> T getBean(String beanName) {
		// 先从缓存中获取，可能该对象已经被初始化
		@SuppressWarnings("unchecked")
		T bean = (T) this.beanFactoryCache.get(beanName);
		if (bean == null) {
			// 如果没有被初始化获取该类的元数据
			if (this.beanDefinitionMap.get(beanName) != null) {
				try {
					// 类的元数据
					BeanDefinition beanDefinition = this.beanDefinitionMap
							.get(beanName);
					// 获取当前类
					Class<?> beanClass = beanDefinition.getBeanClass();
					// 初始化当前对象
					@SuppressWarnings("unchecked")
					T currentObject = (T) beanClass.newInstance();
					// 初始化失败
					if (currentObject == null) {
						Log.getInstance().warm(
								beanClass.getClass().getName() + "null");
						return null;
					}
					// 注入依赖对象
					if (beanDefinition.getRelyOnClass().size() != 0) {
						Iterator<String> bit = beanDefinition.getRelyOnClass()
								.keySet().iterator();
						String key = null;
						while (bit.hasNext()) {
							key = bit.next();
							this.setRely(currentObject, key, this.getBean(key));
						}
					}
					return currentObject;
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else {
				Log.getInstance().warm(
						beanName + " object null at beanFactory 255");
				return null;
			}
		} else {
			return bean;
		}
		return null;
	}

	private void initSystemConfigParameters(String configFilePath)
			throws FileNotFoundException, IOException {
		Cache cache = Cache.getInstance();
		Map<String, Object> systemMessage = new ConcurrentHashMap<String, Object>();
		Properties props = new Properties();
		props.load(new FileInputStream(FileUtil.getInstance()
				.getClassesPhysicPath() + configFilePath));
		Object key = null;
		Iterator<Object> it = props.keySet().iterator();
		while (it.hasNext()) {
			key = it.next();
			systemMessage
					.put(key.toString(), props.getProperty(key.toString()));
		}
		cache.put(CACHE_KEY.CONFIG_FILE, systemMessage);
	}

	public void initBeans() {
		this.initBeans(null);
	}

	public void initBeans(Initializer initializer) {
		Log.getInstance().info("-----------------Ioc容器初始化-------------------");
		try {
			this.initSystemConfigParameters(this.getSystemConfigPath());
			Document doc = Xml.getXmlDocumentByPath(this.getXmlName(),
					"beanFactory.dtd");
			this.initBeanByElement(doc.getDocumentElement());
			if (initializer == null) {
				initializer = BeanFactory.getInstance().getBean(
						SYS_OBJECT_NAME.INITIALIZER_SERVER);

			}
			initializer.init();
		} catch (Exception e) {
			Log.getInstance().error(e);
		}
		Log.getInstance()
				.info("-----------------Ioc容器初始化成功-------------------");
	}

	public void defaultInitBeans(Initializer initializer) {
		this.setSystemConfigPath("system_config.properties");
		this.setContextConfig(FileUtil.getInstance().getClassesPhysicPath()
				+ "beans.xml");
		this.initBeans(initializer);
	}

	public BeanDefinition getBeanDefinition(String beanName) {
		return this.beanDefinitionMap.get(beanName);
	}
}